package com.dc.commons.shiro.credential;

import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;

import com.dc.commons.shiro.listener.DcSessionListener;
import com.dc.commons.shiro.session.DcSession;
import com.dc.commons.shiro.session.DcSession.SessionStatus;
import com.dc.commons.sys.persistence.User;

public class DcHashedCredentialsMatcher extends HashedCredentialsMatcher {

	 private Cache<String, ExcessiveAttempts> passwordRetryCache;
	 private SessionDAO sessionDao;
	 private List<DcSessionListener> dcSessionListeners;
	 
	    public DcHashedCredentialsMatcher(CacheManager cacheManager) {
	        passwordRetryCache = cacheManager.getCache("passwordRetryCache");
	    }
	    
		public void setPasswordRetryCache(Cache<String, ExcessiveAttempts> passwordRetryCache) {
			this.passwordRetryCache = passwordRetryCache;
		}

		public void setSessionDao(SessionDAO sessionDao) {
			this.sessionDao = sessionDao;
		}


		public void setDcSessionListeners(List<DcSessionListener> dcSessionListeners) {
			this.dcSessionListeners = dcSessionListeners;
		}



		@Override
	    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
	        String username = (String)token.getPrincipal();
	        //retry count + 1
	        ExcessiveAttempts excessiveAttempts = passwordRetryCache.get(username);
	        if(excessiveAttempts == null) {
	        	excessiveAttempts = new ExcessiveAttempts();
	            passwordRetryCache.put(username, excessiveAttempts);
	        }
	        if(!excessiveAttempts.valid()) {
	            throw new ExcessiveAttemptsException();
	        }

	        boolean matches = super.doCredentialsMatch(token, info);
	        if(matches) {
	            passwordRetryCache.remove(username);
	            checkUser(token, info);
	        }
	        return matches;
	    }
	    
	    /**
	     * 检查是否存在相同的用户。 可以考虑 通过参数 如果不踢的话可以抛出异常，提示前端。
	     * @param token
	     * @param info
	     */
	    private void checkUser(AuthenticationToken token, AuthenticationInfo info){
	        Collection<Session> sessions=sessionDao.getActiveSessions();
	   		 User shiroUser=(User)info.getPrincipals().getPrimaryPrincipal();
	   		 UsernamePasswordToken uToken=(UsernamePasswordToken)token;
	   		 Session shiroSession=SecurityUtils.getSubject().getSession(false);
	            for(Session session:sessions){
	           	 if(shiroSession.getId().equals(session.getId()))continue;//同一个Session 忽略
	           	 
	            	PrincipalCollection collection=(PrincipalCollection)session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
	            	if(collection==null)continue;
	            	
	            	User user=(User)collection.getPrimaryPrincipal();
	            	if(user!=null&&user.getUserCode().equals(shiroUser.getUserCode())){
	            		DcSession dcSession=(DcSession)sessionDao.readSession(session.getId());
	            		dcSession.validate();
	            		if(dcSession.isDcValid()){//只有这个Session有效的情况下才会设置
	            			dcSession.setStatus(SessionStatus.Forced);
	            			Date date=new Date();
	            			dcSession.setMsg(String.format("你于  %tF %tT 被  %s 从  %s 踢下，如有疑问请于管理员联系！",date,date,shiroUser.getUserName(),uToken.getHost()));
	            			if(dcSessionListeners!=null){
	            				for(DcSessionListener listener:dcSessionListeners){
	            					listener.onForced(shiroUser,user,dcSession,uToken.getHost());
	            				}
	            			}
	            		}
	            	}
	            }
	    }
}
